package net.atomarrow.util;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author: zhangyi
 * @date: 2023/10/7
 */
public class XssFilterUtil {
    private static List<PatternItem> patterns = null;

    /**
     * @param
     * @Title: XSS常见攻击
     * @methodName: getXssPatternList
     * @Description: Pattern.MULTILINE(? m)：在这种模式下，'^'和'$'分别匹配一行的开始和结束。此外，'^'仍然匹配字符串的开始，'$'也匹配字符串的结束。
     * 默认情况下，这两个表达式仅仅匹配字符串的开始和结束。
     * <p>
     * Pattern.DOTALL(?s) ：在这种模式下，表达式'.'可以匹配任意字符，包括表示一行的结束符。
     * 默认情况下，表达式'.'不匹配行的结束符。
     * @author: 王延飞
     * @date: 2019-01-25 9:11
     */
    private static List<Object[]> getXssPatternList() {

        List<Object[]> ret = new ArrayList<Object[]>();

        ret.add(new Object[]{"<(?=(?:no)?script[^>]*)", Pattern.CASE_INSENSITIVE,"＜" });
//        ret.add(new Object[]{"</script>", Pattern.CASE_INSENSITIVE});
//        ret.add(new Object[]{"<script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"<(?=[^<]+eval\\((?:.*?)\\))", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL, "＜" });
        ret.add(new Object[]{"<(?=[^<]+expression\\((?:.*?)\\))", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL, "＜" });
        ret.add(new Object[]{"<(?=[^<]+(?:javascript:|vbscript:|view-source:)+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL, "＜" });
//        ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"<(?=[^<]+(?:oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=.+)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL, "＜"});
        return ret;
    }

    /**
     * @param
     * @return java.util.List<java.util.regex.Pattern>
     * @Title: XSS常见攻击-正则表达式
     * @methodName: getPatterns
     * @Description:
     * @author: 王延飞
     * @date: 2019-01-25 9:11
     */
    private static List<PatternItem> getPatterns() {


        if (patterns == null) {

            List<PatternItem> list = new ArrayList<PatternItem>();

            String regex = null;
            Integer flag = null;
            String replacement = null;
            int arrLength = 0;

            for (Object[] arr : getXssPatternList()) {
                regex = (String) arr[0];
                flag = (Integer) arr[1];
                replacement = (String) arr[2];

                Pattern pattern = Pattern.compile(regex, flag);
                list.add(new PatternItem(pattern, replacement));
            }

            patterns = list;
        }

        return patterns;
    }

    /**
     * @param value
     * @return java.lang.String
     * @Title: 处理特殊字符
     * @methodName: stripXss
     * @Description: 如果是特殊字符，策略有两种：转义或删除
     * @author: 王延飞
     * @date: 2019-01-25 9:12
     */
    public static String stripXss(String value) {
        if (StringUtil.isNotBlank(value)) {


            //
            Matcher matcher = null;

            for (PatternItem item : getPatterns()) {
                matcher = item.pattern.matcher(value);
                // 匹配
                if (matcher.find()) {
                    String group = matcher.group();
                    if (StringUtil.isBlank(group)) continue;
                    // 删除相关字符串
                    value = matcher.replaceAll(item.replacement);
                }
            }


            /**
             * 替换为转移字符，类似HtmlUtils.htmlEscape
             */
//            value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");


        }


        return value;
    }

    private static class PatternItem {
        Pattern pattern;
        String replacement;

        public PatternItem(Pattern pattern, String replacement) {
            this.pattern = pattern;
            this.replacement = replacement;
        }
    }

    public static void main(String[] args) {
//        Pattern pattern = Pattern.compile("<[^<]*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmousout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=.+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
        String input = "<script >var a = 1;</script><script src=''><a href='javascript:eval(1>2)'><p><img src='' onerror=alert(1) /></p><p><img></img><p>";
        System.out.println(stripXss(input));

    }

}
